package com.dc.schedule.client.trigger;

import com.dc.schedule.api.context.FireTaskContext;
import com.dc.schedule.api.model.ClientTaskNotifyMessage;
import com.dc.schedule.api.model.StaticTaskRegistryConfig;
import com.dc.schedule.api.status.TaskExecutionStatus;
import com.dc.schedule.client.context.ClientExecutionContextManager;
import com.dc.schedule.client.context.ClientTaskExecutionContext;
import com.dc.schedule.client.socket.ScheduleSocketClient;
import lombok.extern.slf4j.Slf4j;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;

/***
 * 定时任务触发器
 * @author Diamon.Cheng
 * @date 2022/10/24
 */
@Slf4j
public class ThreadPoolTaskTrigger {
    private final ExecutorService executorService;
    private final Map<String, StaticTaskRegistryConfig> taskMap;
    
    private final ScheduleSocketClient scheduleSocketClient;
    
    private final ClientExecutionContextManager clientExecutionContextManager
            = ClientExecutionContextManager.getINSTANCE();
    
    public ThreadPoolTaskTrigger(ExecutorService executorService,
                                 List<StaticTaskRegistryConfig> tasks,
                                 ScheduleSocketClient scheduleSocketClient) {
        this.executorService = executorService;
        this.taskMap = tasks.stream().collect(Collectors.toMap(
                e -> e.getRegistry().getTaskName(),
                Function.identity(),
                (e1, e2) -> e2
        ));
        this.scheduleSocketClient = scheduleSocketClient;
    }
    
    public void fireTask(FireTaskContext context) {
        final StaticTaskRegistryConfig taskConfig = taskMap.get(context.getTaskName());
        if (taskConfig == null) {
            throw new IllegalStateException("任务 [" + context.getTaskName() + "] 没有找到");
        }
        final ClientTaskExecutionContext executionContext = new ClientTaskExecutionContext(
                context,
                taskConfig
        );
        final Future<?> future = executorService.submit(() -> {
            try {
                // build task context
                clientExecutionContextManager
                        .bindContext(executionContext);
                // send start msg
                final ClientTaskNotifyMessage startedMsg = new ClientTaskNotifyMessage();
                startedMsg.setExecutionId(context.getExecutionId());
                startedMsg.setStatus(TaskExecutionStatus.EXECUTING);
                scheduleSocketClient.sendMessage(startedMsg);
                
                log.info("[定时任务]开始执行任务 {}", context);
                taskConfig.getRunnable().run();
                log.info("[定时任务]结束执行任务 {}", context);
                // send succeed msg
                final ClientTaskNotifyMessage successMessage = new ClientTaskNotifyMessage();
                successMessage.setExecutionId(context.getExecutionId());
                successMessage.setStatus(TaskExecutionStatus.EXECUTED);
                successMessage.setMessage(executionContext.getResultMessage());
                scheduleSocketClient.sendMessage(successMessage);
            } catch (Exception e) {
                log.error("[定时任务]定时任务执行失败! " + context, e);
                // send failed msg
                final ClientTaskNotifyMessage failedMessage = new ClientTaskNotifyMessage();
                failedMessage.setExecutionId(context.getExecutionId());
                failedMessage.setStatus(TaskExecutionStatus.EXECUTE_FAILED);
                failedMessage.setMessage("任务客户端执行失败：  \n" + printEx(e));
                try {
                    scheduleSocketClient.sendMessage(failedMessage);
                } catch (Exception ex) {
                    throw new RuntimeException(ex);
                }
            } finally {
                clientExecutionContextManager.release();
            }
        });
        executionContext.setFuture(future);
    }
    
    private String printEx(Exception ex) {
        final StringWriter stringWriter = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(stringWriter);
        ex.printStackTrace(printWriter);
        return stringWriter.toString();
    }
}
